#include "filelogappender.h"

namespace vlog
{

FileLogAppender::FileLogAppender(const std::string& strFileName)
    : LogAppender(std::make_shared<LogFormatter>()), m_FileName(strFileName), m_FilePath(strFileName), m_isFlash(true)
{
    m_LastOpen = std::chrono::system_clock::now();
    ReOpen();
    if (m_isOpenError)
    {
        std::cout << "reopen file: " << m_FilePath.c_str() << " error "
                  << "file type:" << static_cast<unsigned char>(fs::status(m_FilePath).type()) << std::endl;
    }

    m_Flashlogthread = std::thread([this]() { this->FlashLog(); });
}

FileLogAppender::~FileLogAppender()
{
    {
        std::unique_lock lock(m_mutex);
        m_isFlash = false;
        m_ConditionVariable.notify_one();
    }

    if (m_Flashlogthread.joinable())
    {
        m_Flashlogthread.join();
    }

    if (m_FileStream)
    {
        m_FileStream.close();
    }
}

bool FileLogAppender::ReOpen() noexcept
{
    if (m_FileStream)
    {
        m_FileStream.close();
    }

    m_isOpenError = true;
    if (fs::is_directory(m_FilePath))
    {
        return m_isOpenError;
    }
    else
    {
        m_FileStream.open(m_FilePath, std::ios::app);
        m_isOpenError = !m_FileStream;
        return !m_isOpenError;
    }
    return m_isOpenError;
}

void FileLogAppender::CheckLastOpen() noexcept
{
    auto now = std::chrono::system_clock::now();
    uint64_t fileLastOpenDuration = std::chrono::duration_cast<std::chrono::hours>(now - m_LastOpen).count();
    auto file_last_open = fs::last_write_time(m_FilePath);

    uint64_t fileLastWriteDuration =
        std::chrono::duration_cast<std::chrono::hours>(fs::file_time_type::clock::now() - file_last_open).count();

    if (fileLastOpenDuration > 24 || fileLastWriteDuration > 24)
    {
        using namespace std::literals;
        std::time_t time = std::chrono::system_clock::to_time_t(now - 24h);
        std::stringstream ss;
        ss << std::put_time(std::localtime(&time), "%Y-%m-%d_%H:%M:%S");
        std::string temp = ss.str() + "-" + m_FileName;
        fs::path nowPath(temp);
        fs::copy_file(m_FilePath, nowPath);
        m_FileStream.close();
        fs::remove(m_FilePath);
        ReOpen();
        m_LastOpen = now;
        if (m_isOpenError)
        {
            std::cout << "reopen file: " << m_FilePath.c_str() << " error "
                      << "file type:" << static_cast<unsigned char>(fs::status(m_FilePath).type()) << std::endl;
        }
    }
}

void FileLogAppender::WriteLog(event::LogEvent::ptr pEvent)
{
    std::unique_lock lock(m_mutex);
    m_CurEventBuf.push_back(pEvent);
    if (m_CurEventBuf.size() >= 1000)
    {
        m_EventBufs.push_back(std::move(m_CurEventBuf));
        m_CurEventBuf.clear();
    }
    m_ConditionVariable.notify_one();
}

void FileLogAppender::RealyWriteLog(event::LogEvent::ptr pEvent)
{
    if (m_isOpenError)
    {
        return;
    }
    CheckLastOpen();
    if (m_pLogFormater)
    {
        if (!m_pLogFormater->Formatter(m_FileStream, pEvent))
        {
            std::cout << "[ERROR] FileLogAppender::WriteLog Formatter ERROR" << std::endl;
        }
        // m_FileStream.flush();
    }
}

void FileLogAppender::FlashLog()
{
    while (true)
    {
        std::vector<event::LogEvent::ptr> eventBuf;
        std::unique_lock lock(m_mutex);

        m_ConditionVariable.wait_for(lock, std::chrono::seconds(2),
                                     [this]() { return !m_isFlash || !m_CurEventBuf.empty() || !m_EventBufs.empty(); });

        if (!m_isFlash && m_CurEventBuf.empty() && m_EventBufs.empty())
        {
            break;
        }
        if (!m_EventBufs.empty())
        {
            eventBuf = std::move(m_EventBufs.front());
            m_EventBufs.erase(m_EventBufs.begin());
        }
        else if (!m_CurEventBuf.empty())
        {
            eventBuf = std::move(m_CurEventBuf);
            m_CurEventBuf.clear();
        }
        lock.unlock();
        for (const auto& pEvevt : eventBuf)
        {
            RealyWriteLog(pEvevt);
        }
    }
}

} // namespace vlog
